﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using nRoute.Components.Bindings;
using nRoute.Utilities;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;

namespace nRoute.Behaviors.Interactivity
{
    public abstract class AttachableDependencyObject
        : DependencyObject, IAttachedObject
    {
        private const string ITEM_ALREADY_EXISTS = "Item already exists in the collection";
        private const string DEPENDENCYOBJECT_CAN_ATTACHEDONCE = "IAttachedObject can only be attached once";

        private FrameworkElement _associatedObject;
        private Dictionary<DependencyProperty, BindingInfo> _pendingBindings;
        
#region IAttachedObject related

        FrameworkElement IAttachedObject.AssociatedObject
        {
            get { return _associatedObject; }
        }

        void IAttachedObject.Attach(FrameworkElement attachedObject)
        {
            Guard.ArgumentNotNull(attachedObject, "attachedObject");
            Guard.ArgumentValue(_associatedObject != null, "attachedObject", DEPENDENCYOBJECT_CAN_ATTACHEDONCE);

            _associatedObject = attachedObject;
            ApplyAllPendingBindings();
            this.OnAttached();
        }

        void IAttachedObject.Detach()
        {
            this.OnDetaching();
            ClearAllBindings();
            _associatedObject = null;
        }

#endregion

#region Overridable

        // note type FrameworkElement, and not object
        protected FrameworkElement AssociatedObject
        {
            get { return _associatedObject; }
        }

        protected virtual void OnAttached() { }

        protected virtual void OnDetaching() { }

#endregion

#region Binding Helpers
        
        protected void SetBinding<P>(DependencyProperty bindingProperty, Binding value)
        {
            SetBinding(bindingProperty, typeof(P), value);
        }

        protected void SetBinding(DependencyProperty bindingProperty, Type bindingType, Binding value)
        {
            if (this.AssociatedObject != null)
            {
                this.SetAttachedBinding(AssociatedObject, bindingProperty, bindingType, value);
            }
            else
            {
                if (_pendingBindings == null)
                    _pendingBindings = new Dictionary<DependencyProperty, BindingInfo>();
                _pendingBindings.Add(bindingProperty,
                    new BindingInfo() { BindingProperty = bindingProperty, BindingType = bindingType, Binding = value });
            }
        }

        protected Binding GetBinding(DependencyProperty bindingProperty)
        {
            if (this.AssociatedObject != null)
            {
                return this.GetAttachedBinding(bindingProperty);
            }
            else if (_pendingBindings != null && _pendingBindings.ContainsKey(bindingProperty))
            {
                return _pendingBindings[bindingProperty].Binding;
            }
            else
            {
                return null;
            }
        }

        protected void ApplyAllPendingBindings()
        {
            if (_pendingBindings == null || _pendingBindings.Count == 0) return;

            // set all bindings
            foreach (var _bindingInfo in _pendingBindings.Values)
            {
                this.SetAttachedBinding(AssociatedObject, _bindingInfo);
            }

            // clear the pending
            _pendingBindings.Clear();
            _pendingBindings = null;
        }

        protected void ClearAllBindings()
        {
            if (_pendingBindings != null) _pendingBindings = null;

            // using binding on self, as opposed to attached bindings, hence disabled this (24-6-2012)
            this.ClearAttachedBindings();
        }

#endregion

    }
}
